/*
在状态模式（State Pattern）中，类的行为是基于它的状态改变的。
这种类型的设计模式属于行为型模式。

在状态模式中，我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。

意图：允许对象在内部状态发生改变时改变它的行为，对象看起来好像修改了它的类。

主要解决：对象的行为依赖于它的状态（属性），并且可以根据它的状态改变而改变它的相关行为。

何时使用：代码中包含大量与对象状态有关的条件语句。

如何解决：将各种具体的状态类抽象出来。

关键代码：通常命令模式的接口中只有一个方法。
        而状态模式的接口中有一个或者多个方法。
        而且，状态模式的实现类的方法，一般返回值，或者是改变实例变量的值。
        也就是说，状态模式一般和对象的状态有关。
        实现类的方法有不同的功能，覆盖接口中的方法。
        状态模式和命令模式一样，也可以用于消除 if...else 等条件选择语句。

应用实例： 1、打篮球的时候运动员可以有正常状态、不正常状态和超常状态。
         2、曾侯乙编钟中，'钟是抽象接口','钟A'等是具体状态，'曾侯乙编钟'是具体环境（Context）。

优点： 1、封装了转换规则。
      2、枚举可能的状态，在枚举状态之前需要确定状态种类。
      3、将所有与某个状态有关的行为放到一个类中，并且可以方便地增加新的状态，只需要改变对象状态即可改变对象的行为。
      4、允许状态转换逻辑与状态对象合成一体，而不是某一个巨大的条件语句块。
      5、可以让多个环境对象共享一个状态对象，从而减少系统中对象的个数。

缺点： 1、状态模式的使用必然会增加系统类和对象的个数。
      2、状态模式的结构与实现都较为复杂，如果使用不当将导致程序结构和代码的混乱。
      3、状态模式对"开闭原则"的支持并不太好，对于可以切换状态的状态模式，增加新的状态类需要修改那些负责状态转换的源代码，否则无法切换到新增状态，而且修改某个状态类的行为也需修改对应类的源代码。

使用场景： 1、行为随状态改变而改变的场景。 2、条件、分支语句的代替者。

注意事项：在行为受状态约束的时候使用状态模式，而且状态不超过 5 个。
*/


/*
war3里面的国家对抗，是一个建造类游戏。里面的建筑在不同的时候有不同的状态，
例如兵营在产兵的时候会发白光，受伤的时候会着火，建造的时候有建筑特效。
兵营在建造没完成的时候不能升级和产兵，升级的时候不能产兵，受伤着火了也能继续产兵或升级。
假设这些建筑的状态和表现为：建造-建造特效、受伤-受伤着火、升级-升级蓝光、生成-生产白光、修复-修复绿光。
*/


#include <map>
#include <iostream>
using namespace std;

/// 状态类
class absState{
public:
    absState() {}
    virtual ~absState() {}
    virtual void show() = 0;
};

class buildingState:public absState{
public:
    void show()
    {
        cout<<"建造特效  ";
    }
};

class fireState:public absState{
public:
    void show()
    {
        cout<<"受伤着火  ";
    }
};

class productionState:public absState{
public:
    void show()
    {
        cout<<"生产白光  ";
    }
};

class upGradeState:public absState
{
public:
    void show()
    {
        cout<<"升级蓝光  ";
    }
};

class repairState:public absState
{
public:
    void show()
    {
        cout<<"修复绿光  ";
    }
};

/// 建筑实现代码


/// 抽象的建筑类
class absBuilding
{
public:
    enum class StateType : uint32_t
    {
        BUILDING=0,
        INJURED,
        REPAIR,
        UPGRADE,
        PRODUCTION
    };
    absBuilding(const std::string &name):mName(name){}
    virtual ~absBuilding(){}

    virtual void show() {
        cout << mName.c_str() << "表现:" << endl;
        auto beg = mStates.begin();
        for ( ; beg != mStates.end(); beg++)
        {
            beg->second->show();
        }

        cout<<endl;
    }

    //实现删除一个特效
    void eraseState(StateType type) {
        /// 下面哪种写法好
#if 0
        auto result = mStates.find(type);
        if(result != mStates.end()) {
            mStates.erase(result);
        }
#else
        mStates.erase(type);
#endif
    }
protected:
    std::string mName;
    std::map<StateType, absState*> mStates;
};

/// 建筑物实例
class ConcreteBuilding:public absBuilding
{
public:
    ConcreteBuilding(const std::string &name):absBuilding(name) {
        mStates[StateType::BUILDING] = new buildingState();
    }
    ~ConcreteBuilding() {}

    /// 升级
    void upGrade(){
        auto result = mStates.find(StateType::UPGRADE);
        if(result == mStates.end())
        {
            mStates[StateType::UPGRADE] = new upGradeState();
        }
        eraseState(StateType::BUILDING);   //升级的时候必须是建造过程已经结束，所以需要删掉“建筑”状态
        eraseState(StateType::PRODUCTION); //升级的时候不能进行生产
    }

    /// 生产
    void production(){
        auto result = mStates.find(StateType::PRODUCTION);
        if(result == mStates.end())      // 判断是否生产中
        {
            mStates[StateType::PRODUCTION] = new upGradeState();
        }
        eraseState(StateType::BUILDING); //生产的时候必须已经建造完成
        eraseState(StateType::UPGRADE);  //生产的时候不能升级
    }

    /// 受伤的时候
    void injured() {
        auto result = mStates.find(StateType::INJURED);
        if(result == mStates.end())
        {
            mStates[StateType::INJURED] = new fireState();
        }
    }
    /// 自我修复
    void repair() {
        auto injuredVal = mStates.find(StateType::INJURED);
        if(injuredVal == mStates.end())
            return;
        auto result = mStates.find(StateType::REPAIR);
        if(result == mStates.end())
        {
            mStates[StateType::REPAIR] = new repairState();
        }
    }
};



int main()
{
    ConcreteBuilding cb("兵营");
    cb.production();
    cb.injured();
    cb.repair();
    cb.show();
    return 0;
}
